package main

import (
	"bufio"
	"crypto/hmac"
	"crypto/md5"
	"crypto/sha1"
	"encoding/base64"
	"encoding/json"
	"net/http"
	"net/url"
	"os"
	_ "peilian-api/bootstrap"
	"strconv"
	"sync"

	"github.com/gin-gonic/gin"
	"github.com/go-redis/redis"
	"github.com/google/uuid"
	"golang.org/x/net/websocket"

	"context"
	"fmt"
	"io/ioutil"
	"os/exec"
	"peilian-api/app/global/common"
	"peilian-api/app/global/databases/tables"
	"peilian-api/app/global/log"
	"peilian-api/app/global/variable"
	"peilian-api/app/services/report_service"
	"peilian-api/utils/sdk"
	"strings"
	"time"
)

var allFiles []string

var HOST = "rtasr.xfyun.cn/v1/ws"

var APPID = "c01ca6f9"
var APPKEY = "def21fdfa59f4353c061b7041084cdaa"

// 结束标识

var END_TAG = "{\"end\": true}"

// 每次发送的数据大小

var SLICE_SIZE = 1280
var wg sync.WaitGroup

var workers = 1
var endTag int

func optionFfmpegToMp3(pcmUrl string) (string, error) {
	s := strings.Split(pcmUrl, "/")
	out := s[len(s)-1:][0]
	out = strings.Replace(out, ".pcm", ".mp3", -1)
	fmt.Println("转MP3的原pcm:" + variable.Config.UfsInfo.UfsUrl + variable.Config.UfsInfo.BucketPrefix + pcmUrl)
	if !ffmpegToMp3(variable.Config.UfsInfo.UfsUrl+variable.Config.UfsInfo.BucketPrefix+pcmUrl, out) {
		return "", fmt.Errorf("视频转存失败")
	}
	// 文件上传
	pcmUrl = strings.Replace(pcmUrl, ".pcm", ".mp3", -1)
	pcmUrl = strings.Replace(pcmUrl, "video", "mp3", -1)
	fileName := fmt.Sprintf("%s%s", variable.Config.UfsInfo.BucketPrefix, pcmUrl)
	b, _ := ioutil.ReadFile(out)
	if err := sdk.IOput(b, fileName, "audio/mpeg"); err != nil {
		return "", fmt.Errorf("文件转存失败：fileName:" + fileName)
	}
	os.Remove(out)
	return fileName, nil
}

func ffmpegToMp3(originUrl string, out string) bool {
	cmdArguments := []string{"-y", "-ac", "1", "-ar", "16000", "-f", "s16le", "-i", originUrl, "-c:a", "libmp3lame", "-q:a", "2", out}
	c := exec.Command("ffmpeg", cmdArguments...)
	err := c.Run()
	return err == nil
}

// 刷历史数据
func main() {
	var trs []tables.RealQues
	query := "介绍一下"
	d := variable.DB.MySQL.Model(tables.RealQues{}).Where("question_text like ?", "%"+query+"%").Find(&trs)
	// d := variable.DB.MySQL.Model(tables.RealQues{}).Where("id = 12476").Find(&trs)
	if d.Error != nil {
		fmt.Println("查询错误")
		return
	}
	if len(trs) == 0 {
		fmt.Println("无需处理")
		return
	}

	ctx := &gin.Context{}
	uuidStr := strings.ReplaceAll(uuid.NewString(), "-", "")
	ctx.Set(common.TraceKey, context.WithValue(context.Background(), common.TraceKey, &log.Trace{TraceId: uuidStr, SpanId: uuidStr, Caller: ""}))

	for i := 0; i < len(trs); i++ {
		if len(trs[i].AnswerText) == 0 {
			continue
		}
		// 现将数据进行翻译屏蔽
		rqId := trs[i].ID
		// 连接数据
		wg.Add(workers)
		for i := 0; i < workers; i++ {
			go StartConn(1, strings.ReplaceAll(variable.Config.UfsInfo.UfsUrl+variable.Config.UfsInfo.BucketPrefix+trs[i].AnswerAudio, "_pal.pcm", ".pcm"), int(rqId))
			fmt.Println(i)
		}
		wg.Wait()
		// 处理过的fileName
		fileName, err := new(report_service.MaskService).MaskWords(ctx, int(rqId))
		if err != nil {
			fmt.Println(err)
		}
		fmt.Println("处理过的file-name:" + fileName)
		// 转mp3
		mp3Name, err := optionFfmpegToMp3(fileName)
		if err != nil {
			fmt.Println(err)
		}
		if len(fileName) != 0 && len(mp3Name) != 0 {
			var mapWhere = make(map[string]interface{}, 0)
			mapWhere["audio"] = variable.Config.UfsInfo.UfsUrl + mp3Name
			mapWhere["answer_audio"] = fileName
			variable.DB.MySQL.Model(tables.RealQues{}).Where("id = ?", rqId).Updates(mapWhere)
		}

	}
}

func StartConn(workerCode int, filePath string, rid int) {
	t1 := time.Now()
	ts := strconv.FormatInt(time.Now().Unix(), 10)
	mac := hmac.New(sha1.New, []byte(APPKEY))
	strByte := []byte(APPID + ts)
	strMd5Byte := md5.Sum(strByte)
	strMd5 := fmt.Sprintf("%x", strMd5Byte)
	mac.Write([]byte(strMd5))
	signa := url.QueryEscape(base64.StdEncoding.EncodeToString(mac.Sum(nil)))
	requestParam := "appid=" + APPID + "&ts=" + ts + "&signa=" + signa + "&lang=cn"
	conn, err := websocket.Dial("wss://"+HOST+"?"+requestParam, websocket.SupportedProtocolVersion, "http://"+HOST)
	if err != nil {
		fmt.Println(err)
		return
	}
	var message string
	websocket.Message.Receive(conn, &message)
	var m map[string]string
	err = json.Unmarshal([]byte(message), &m)
	if err != nil {
		println(err.Error())
		return
	} else if m["code"] != "0" {
		fmt.Println("handshake fail!" + message)
		return
	}
	defer conn.Close()
	sendChan := make(chan int, 1)

	readChan := make(chan string, 1)
	defer close(sendChan)
	defer close(readChan)
	//fmt.Println("文件路径: " + filePath)
	go send(conn, sendChan, filePath)
	go receive(conn, readChan, workerCode, rid)
	<-sendChan
	<-readChan
	//fmt.Println(text)
	wg.Done()
	t2 := time.Now()
	fmt.Println(fmt.Sprintf("[%d]一共用了： ", workerCode), t2.Unix()-t1.Unix())
}

func send(conn *websocket.Conn, sendChan chan int, filePath string) {
	// 分片上传音频
	defer func() {
		sendChan <- 1
	}()
	data := make([]byte, SLICE_SIZE)
	resp, err := http.Get(filePath)
	defer resp.Body.Close()
	reader := bufio.NewReaderSize(resp.Body, 1280*32)
	// audio, err := os.Open(filePath)
	// defer audio.Close()
	if err != nil {
		fmt.Println("open file error: " + err.Error())
	}
	var count = 0
	for i, e := reader.Read(data); i > 0; i, e = reader.Read(data) {
		if e != nil {
			if e.Error() == "EOF" {
				// 读到结束符
				break
			}
			fmt.Println("read file error: " + e.Error())
			return
		}
		count++
		if count >= 60 && count <= 72 {
			data = make([]byte, 1280)
		}
		if err := websocket.Message.Send(conn, data[:i]); err != nil {
			fmt.Println("send byte msg err: ", err)
			return
		}
		time.Sleep(40 * time.Millisecond)
	}

	// 上传结束符
	if err := websocket.Message.Send(conn, END_TAG); err != nil {
	} else {
		println("send end tag success, ", len(END_TAG))
	}
}

func receive(conn *websocket.Conn, readChan chan string, workerCode int, rid int) {
	variable.DB.Redis.Del(fmt.Sprintf(common.RedisSetMaskRIdKey, rid)) // 写数据之前，先删除缓存
	text := ""
	for {
		var msg []byte
		var result map[string]string
		if err := websocket.Message.Receive(conn, &msg); err != nil {
			if err.Error() == "EOF" {
				fmt.Println("receive data end --", time.Now().Unix())
			} else {
				fmt.Println("receive msg error: ", err.Error())
			}

			break
		}

		err := json.Unmarshal(msg, &result)
		if err != nil {
			println(string(msg))
			fmt.Println("response json parse error")
			continue
		}

		if result["code"] == "0" {
			var asrResult AsrResult
			err := json.Unmarshal([]byte(result["data"]), &asrResult)
			if err != nil {
				println("receive msg: ", string(msg))

				break
			}
			var wsText = WsText{}
			if asrResult.Cn.St.Type == "0" {
				for _, wse := range asrResult.Cn.St.Rt[0].Ws {
					// fmt.Printf("%+v", wse)
					var winfo WsInfo
					winfo.Bg = asrResult.Cn.St.Bg
					winfo.Wb = wse.Wb
					var t = ""
					for _, cwe := range wse.Cw {
						text += cwe.W
						t += cwe.W
					}
					winfo.Word = t
					winfo.We = wse.We
					winfo.Wb = wse.Wb
					winfo.Ed = asrResult.Cn.St.Ed
					// 写入
					wsText.Wi = append(wsText.Wi, winfo)
				}

				b, _ := json.Marshal(wsText) // 写出去
				// 模拟写入redis
				var wt = WsText{}
				json.Unmarshal(b, &wt)

				// 写入redis
				for i := 0; i < len(wt.Wi); i++ {
					bg, _ := strconv.ParseFloat(wt.Wi[i].Bg, 64)
					wb := wt.Wi[i].Wb
					b, _ := json.Marshal(wt.Wi[i])
					variable.DB.Redis.ZAdd(fmt.Sprintf(common.RedisSetMaskRIdKey, rid), redis.Z{Member: string(b), Score: bg + wb*10}) // 只写内容到redis
				}

			}
			if asrResult.Ls {
				endTag += 1
				fmt.Println(fmt.Sprintf("[%d]收到结束标志 ", workerCode), endTag, "  ", time.Now().Unix())
			}

		} else {
			println("invalid result: ", string(msg))
		}
	}
	readChan <- text
	// 更新一次数据
	var mapWhere = make(map[string]interface{}, 0)
	mapWhere["answer_text"] = text
	variable.DB.MySQL.Model(tables.RealQues{}).Where("id = ?", rid).Updates(mapWhere)
	fmt.Println("语音翻译:" + text)
}

type AsrResult struct {
	Cn    Cn      `json:"cn"`
	SegId float64 `json:"seg_id"`
	Ls    bool    `json:"ls"`
}

type Cn struct {
	St St `json:"st"`
}

type St struct {
	Bg   string      `json:"bg"`
	Ed   string      `json:"ed"`
	Type string      `json:"type"`
	Rt   []RtElement `json:"rt"`
}

type RtElement struct {
	Ws []WsElement `json:"ws"`
}

type WsElement struct {
	Wb float64     `json:"wb"`
	We float64     `json:"we"`
	Cw []CwElement `json:"cw"`
}

type CwElement struct {
	W  string `json:"w"`
	Wp string `json:"wp"`
}

type WsInfo struct {
	Bg   string  `json:"bg"`
	Ed   string  `json:"ed"`
	Wb   float64 `json:"wb"`
	We   float64 `json:"we"`
	Word string  `json:"word"`
}

type WsText struct {
	Bg   string `json:"bg"`
	Ed   string `json:"ed"`
	Wi   []WsInfo
	Text string `json:"text"`
}

// 记录时间
type WsRecord struct {
	Begin float64 `json:"begin"`
	End   float64 `json:"end"`
}
